﻿using System;
using System.Globalization;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;

namespace Als.Common.File
{
    public static class FilesHelper
    {
        #region 文件转MemoryStream
        /// <summary>
        /// 文件转成二进制流
        /// </summary>
        /// <param name="filepath">文件相对地址</param>
        /// <returns></returns>
        public static MemoryStream ToMemoryStream(this string filepath)
        {
            var fs = new FileStream(filepath.GetMapPath() ?? throw new InvalidOperationException("文件地址错误"), FileMode.Open, FileAccess.Read);
            var fileContentByte = new byte[fs.Length];
            fs.Read(fileContentByte, 0, Convert.ToInt32(fs.Length));
            fs.Close();
            return new MemoryStream(fileContentByte);
        }

        #endregion

        #region 文件转FileStream

        /// <summary>
        /// 文件转成二进制流
        /// </summary>
        /// <param name="filepath"></param>
        /// <returns></returns>
        public static FileStream ToFileStream(this string filepath)
        {
            FileStream fileStream = null;
            if (string.IsNullOrEmpty(filepath)) return null;
            filepath = filepath.GetMapPath();
            if (System.IO.File.Exists(filepath))
            {
                fileStream = new FileStream(filepath.GetMapPath(), FileMode.Open);
            }
            return fileStream;
        }

        #endregion

        #region 获取文件绝对路径

        /// <summary>
        /// 获取文件绝对路径(自动识别WEB和桌面程序)
        /// </summary>
        /// <param name="strPath">文件相对地址</param>
        /// <returns></returns>
        private static string GetMapPath(this string strPath)
        {
            if (HttpContext.Current != null)
            {
                return HttpContext.Current.Server.MapPath($"~{strPath}");
            }
            else //非web程序引用             
            {
                strPath = strPath.Replace("/", "\\");
                if (strPath.StartsWith("\\"))
                {
                    strPath = strPath.TrimStart('\\');
                }
                return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, strPath);
            }
        }

        #endregion

        #region MD5签名验证

        /// <summary>
        /// 获取文件的MD5值
        /// </summary>
        /// <param name="fileName"> 文件名 </param>
        /// <returns> 32位MD5 </returns>
        public static string GetFileMd5(this string fileName)
        {
            var fs = new FileStream(fileName.GetMapPath(), FileMode.Open, FileAccess.Read, FileShare.Read);
            const int bufferSize = 1024 * 1024;
            var buffer = new byte[bufferSize];
            var md5 = new MD5CryptoServiceProvider();
            md5.Initialize();
            long offset = 0;
            while (offset < fs.Length)
            {
                long readSize = bufferSize;
                if (offset + readSize > fs.Length)
                {
                    readSize = fs.Length - offset;
                }
                fs.Read(buffer, 0, (int)readSize);
                if (offset + readSize < fs.Length)
                {
                    md5.TransformBlock(buffer, 0, (int)readSize, buffer, 0);
                }
                else
                {
                    md5.TransformFinalBlock(buffer, 0, (int)readSize);
                }
                offset += bufferSize;
            }
            fs.Close();
            var result = md5.Hash;
            md5.Clear();
            var sb = new StringBuilder(32);
            foreach (byte b in result)
            {
                sb.Append(b.ToString("X2"));
            }
            return sb.ToString();
        }


        /// <summary>
        /// 对给定文件路径的文件加上标签
        /// </summary>
        /// <param name="path">要加密的文件的路径</param>
        /// <returns>标签的值</returns>
        public static bool FileAddMd5(this string path)
        {
            var isNeed = !FileCheckMd5(path);
            try
            {
                var fsread = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
                var md5File = new byte[fsread.Length];
                fsread.Read(md5File, 0, (int)fsread.Length);// 将文件流读取到Buffer中
                fsread.Close();
                if (isNeed)
                {
                    var result = Md5Buffer(md5File, 0, md5File.Length);// 对Buffer中的字节内容算MD5
                    byte[] md5 = System.Text.Encoding.ASCII.GetBytes(result);// 将字符串转换成字节数组以便写人到文件中
                    var fsWrite = new FileStream(path, FileMode.Open, FileAccess.ReadWrite);
                    fsWrite.Write(md5File, 0, md5File.Length);// 将文件，MD5值 重新写入到文件中。
                    fsWrite.Write(md5, 0, md5.Length);
                    fsWrite.Close();
                }
                else
                {
                    var fsWrite = new FileStream(path, FileMode.Open, FileAccess.ReadWrite);
                    fsWrite.Write(md5File, 0, md5File.Length);
                    fsWrite.Close();
                }
            }
            catch
            {
                return false;
            }
            return true;
        }
        
        /// <summary>
        /// 对给定路径的文件进行验证
        /// </summary>
        /// <param name="path"></param>
        /// <returns>是否加了标签或是否标签值与内容值一致</returns>
        public static bool FileCheckMd5(string path)
        {
            try
            {
                var getFile = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
                var md5File = new byte[getFile.Length];// 读入文件
                getFile.Read(md5File, 0, (int)getFile.Length);
                getFile.Close();

                var result = Md5Buffer(md5File, 0, md5File.Length - 32);// 对文件除最后32位以外的字节计算MD5，这个32是因为标签位为32位。
                var md5 = System.Text.Encoding.ASCII.GetString(md5File, md5File.Length - 32, 32);//读取文件最后32位，其中保存的就是MD5值
                return result == md5;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 计算文件的MD5值
        /// </summary>
        /// <param name="md5File">MD5签名文件字符数组</param>
        /// <param name="index">计算起始位置</param>
        /// <param name="count">计算终止位置</param>
        /// <returns>计算结果</returns>
        private static string Md5Buffer(byte[] md5File, int index, int count)
        {
            System.Security.Cryptography.MD5CryptoServiceProvider getMd5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
            var hashByte = getMd5.ComputeHash(md5File, index, count);
            var result = BitConverter.ToString(hashByte);
            result = result.Replace("-", "");
            return result;
        }
        #endregion

        #region 文件加密解密

        /// <summary>
        /// 加密文件
        /// </summary>
        /// <param name="filePath">输入文件路径</param>
        /// <param name="savePath">加密后输出文件路径</param>
        /// <param name="keyStr">密码，可以为空默认 masoft.cn</param>
        /// <returns></returns>  
        public static bool EncryptFile(string filePath, string savePath, string keyStr)
        {
            var des = new DESCryptoServiceProvider();
            if (keyStr == "")
                keyStr = "masoft.cn";
            var fs = System.IO.File.OpenRead(filePath);
            var inputByteArray = new byte[fs.Length];
            fs.Read(inputByteArray, 0, (int)fs.Length);
            fs.Close();
            var keyByteArray = Encoding.Default.GetBytes(keyStr);
            SHA1 ha = new SHA1Managed();
            var hb = ha.ComputeHash(keyByteArray);
            var sKey = new byte[8];
            var sIv = new byte[8];
            for (var i = 0; i < 8; i++)
                sKey[i] = hb[i];
            for (var i = 8; i < 16; i++)
                sIv[i - 8] = hb[i];
            des.Key = sKey;
            des.IV = sIv;
            var ms = new MemoryStream();
            var cs = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);
            cs.Write(inputByteArray, 0, inputByteArray.Length);
            cs.FlushFinalBlock();
            fs = System.IO.File.OpenWrite(savePath);
            foreach (var b in ms.ToArray())
            {
                fs.WriteByte(b);
            }
            fs.Close();
            cs.Close();
            ms.Close();
            return true;
        }

        /// <summary>
        /// 解密文件
        /// </summary>
        /// <param name="filePath">输入文件路径</param>
        /// <param name="savePath">解密后输出文件路径</param>
        /// <param name="keyStr">密码，可以为空默认 masoft.cn</param>
        /// <returns></returns>    
        public static  bool DecryptFile(string filePath, string savePath, string keyStr)
        {
            var des = new DESCryptoServiceProvider();
            if (keyStr == "")
                keyStr = "masoft.cn";
            var fs = System.IO.File.OpenRead(filePath);
            var inputByteArray = new byte[fs.Length];
            fs.Read(inputByteArray, 0, (int)fs.Length);
            fs.Close();
            var keyByteArray = Encoding.Default.GetBytes(keyStr);
            SHA1 ha = new SHA1Managed();
            var hb = ha.ComputeHash(keyByteArray);
            var sKey = new byte[8];
            var sIv = new byte[8];
            for (var i = 0; i < 8; i++)
                sKey[i] = hb[i];
            for (var i = 8; i < 16; i++)
                sIv[i - 8] = hb[i];
            des.Key = sKey;
            des.IV = sIv;
            var ms = new MemoryStream();
            var cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write);
            cs.Write(inputByteArray, 0, inputByteArray.Length);
            cs.FlushFinalBlock();
            fs = System.IO.File.OpenWrite(savePath);
            foreach (var b in ms.ToArray())
            {
                fs.WriteByte(b);
            }
            fs.Close();
            cs.Close();
            ms.Close();
            return true;
        }

        #endregion

        #region 网页下载图片

        /// <summary>
        /// 获取图片标志
        /// </summary>
        private static string[] GetImgTag(string htmlStr)
        {
            var regObj = new Regex("<img.+?>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
            var strAry = new string[regObj.Matches(htmlStr).Count];
            var i = 0;
            foreach (Match matchItem in regObj.Matches(htmlStr))
            {
                strAry[i] = GetImgUrl(matchItem.Value);
                i++;
            }
            return strAry;
        }

        /// <summary>
        /// 获取图片URL地址
        /// </summary>
        private static string GetImgUrl(string imgTagStr)
        {
            var str = "";
            var regObj = new Regex("http://.+.(?:jpg|gif|bmp|png)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
            foreach (Match matchItem in regObj.Matches(imgTagStr))
            {
                str = matchItem.Value;
            }
            return str;
        }

        /// <summary>
        /// 下载图片到本地
        /// </summary>
        /// <param name="strHtml">HTML</param>
        /// <param name="path">路径</param>
        public static string SaveUrlPics(string strHtml, string path)
        {
            var nowym = DateTime.Now.ToString("yyyy-MM");  //当前年月
            var nowdd = DateTime.Now.ToString("dd");       //当天号数
            path = path + nowym + "/" + nowdd;
            if (!Directory.Exists(path)) Directory.CreateDirectory(path);

            var imgurlAry = GetImgTag(strHtml);
            try
            {
                foreach (var t in imgurlAry)
                {
                    var preStr = DateTime.Now.ToString(CultureInfo.InvariantCulture) + "_";
                    preStr = preStr.Replace("-", "");
                    preStr = preStr.Replace(":", "");
                    preStr = preStr.Replace(" ", "");
                    var wc = new WebClient();
                    wc.DownloadFile(t, path + "/" + preStr + t.Substring(t.LastIndexOf("/", StringComparison.Ordinal) + 1));
                }
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
            return strHtml;
        }

        #endregion

        #region 从文件中读取所有内容

        /// <summary>
        /// 从文件中读取所有内容
        /// 如果文件不存在，返回空字符串
        /// </summary>
        /// <param name="filePath">filePath：完整路径，如D:\Temp\Temp.json</param>
        /// <param name="encoding">编码</param>
        /// <returns></returns>
        public static string Read(string filePath, Encoding encoding = null)
        {
            if (!System.IO.File.Exists(filePath)) return "";
            if (encoding == null)
            {
                encoding = Encoding.UTF8;
            }
            return System.IO.File.ReadAllText(filePath, encoding);
        }

        #endregion

        #region 往文件中写内容

        /// <summary>
        /// 往文件中写内容
        /// 如果文件已存在，则会删除文件再创建。反之直接创建文件
        /// </summary>
        /// <Param name="filePath">完整路径，如D:\Temp\Temp.json</Param>
        /// <Param name="content">内容。可以\r\n换行。</Param>
        /// <returns></returns>
        public static bool Write(string filePath, string content, Encoding encoding = null)
        {
            if (filePath.StartsWith("~") || filePath.StartsWith("/"))
            {
                filePath = AppDomain.CurrentDomain.RelativeSearchPath;
            }
            System.IO.File.Delete(filePath);
            using (var fs = new FileStream(filePath, FileMode.Append, FileAccess.Write))
            {
                if (encoding == null)
                {
                    encoding = Encoding.UTF8;
                }
                var sw = new StreamWriter(fs, encoding);
                sw.Write(content);
                sw.Flush();
                sw.Close();
            }
            return true;
        }

        #endregion

        #region 文件复制

        /// <summary>
        /// 文件复制
        /// </summary>
        /// <param name="fromDir">原地址</param>
        /// <param name="toDir">目的地址</param>
        public static void Copy(string fromDir, string toDir)
        {
            var source = new DirectoryInfo(fromDir);
            var target = new DirectoryInfo(toDir);
            Copy(source, target);
        }

        /// <summary>
        /// 文件夹复制
        /// </summary>
        /// <param name="from"></param>
        /// <param name="to"></param>
        public static void Copy(DirectoryInfo from, DirectoryInfo to)
        {
            if (to.FullName.StartsWith(from.FullName, StringComparison.CurrentCultureIgnoreCase))
            {
                throw new Exception("父目录不能拷贝到子目录！");
            }

            if (!from.Exists)
            {
                return;
            }
            if (!to.Exists)
            {
                to.Create();
            }
            var files = from.GetFiles();
            foreach (var t in files)
            {
                System.IO.File.Copy(t.FullName, Path.Combine(to.FullName, t.Name), true);
            }
            var dirs = from.GetDirectories();
            foreach (var t in dirs)
            {
                Copy(t.FullName, Path.Combine(to.FullName, t.Name));
            }
        }

        #endregion

        #region 输出文件

        /// <summary>
        /// 输出图片
        /// </summary>
        /// <param name="fs"></param>
        public static void SendImage(FileStream fs)
        {
            using (var img = System.Drawing.Image.FromStream(fs))
            {
                using (var ms = new MemoryStream())
                {
                    img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
                    HttpContext.Current.Response.ClearContent();
                    HttpContext.Current.Response.BinaryWrite(ms.ToArray());
                    HttpContext.Current.Response.ContentType = "image/jpeg";//指定输出格式为图形
                }
            }
        }

        /// <summary>
        /// 输出文件
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <param name="saveText">内容</param>
        public static void SendFile(string fileName, string saveText)
        {
            var bs2 = Encoding.GetEncoding("GB2312").GetBytes(saveText);
            var ms2 = new MemoryStream(bs2, 0, bs2.Length, true, true);
            SendFile(fileName, ms2, Encoding.UTF8);
        }

        /// <summary>
        /// 把内存流（文件）发送到客户端
        /// </summary>
        /// <param name="fileName">文件名称</param> 
        /// <param name="ms">文件流</param>
        /// <param name="encoding"></param>
        public static void SendFile(string fileName, MemoryStream ms, Encoding encoding = null)
        {
            encoding = encoding ?? Encoding.UTF8;
            if (ms == null || string.IsNullOrEmpty(fileName)) return;
            var response = HttpContext.Current.Response;
            response.Clear();
            response.AddHeader("Content-Type", "application/octet-stream");
            response.Charset = encoding.BodyName;// "utf-8";
            if (HttpContext.Current.Request.UserAgent != null && (!HttpContext.Current.Request.UserAgent.Contains("Firefox") && !HttpContext.Current.Request.UserAgent.Contains("Chrome")))
            {
                fileName = HttpUtility.UrlEncode(fileName, encoding);
            }
            response.AddHeader("Content-Disposition", "attachment;filename=" + fileName);
            response.BinaryWrite(ms.GetBuffer());
            ms.Close();
            ms.Dispose();
            response.Flush();
            response.End();
        }

        #endregion





    }
}
